package cn.tedu.spotms.server.service.impl;

import cn.tedu.spotms.server.ex.ServiceException;
import cn.tedu.spotms.server.mapper.AdminMapper;
import cn.tedu.spotms.server.mapper.RoleMapper;
import cn.tedu.spotms.server.pojo.dto.AdminAddNewDTO;
import cn.tedu.spotms.server.pojo.dto.AdminLoginDTO;
import cn.tedu.spotms.server.pojo.dto.AdminMatchRolesDTO;
import cn.tedu.spotms.server.pojo.dto.AdminUpdateInfoDTO;
import cn.tedu.spotms.server.pojo.entity.Admin;
import cn.tedu.spotms.server.pojo.vo.AdminDetailVO;
import cn.tedu.spotms.server.pojo.vo.AdminListItemVO;
import cn.tedu.spotms.server.pojo.vo.AdminSimpleVO;
import cn.tedu.spotms.server.pojo.vo.RoleListItemVO;
import cn.tedu.spotms.server.security.JwtConst;
import cn.tedu.spotms.server.service.IAdminService;
import cn.tedu.spotms.server.web.State;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {

    @Value("${spotms.jwt.secret-key}")
    private String secretKey;

    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        // 准备被认证数据
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(
                adminLoginDTO.getUsername(), adminLoginDTO.getPassword());
        // 调用AuthenticationManager验证用户名与密码
        // 执行认证，如果此过程没有抛出异常，则表示认证通过，如果认证信息有误，将抛出异常
        Authentication authenticate = authenticationManager.authenticate(authentication);

        // 如果程序可以执行到此处，则表示登录成功
        // 生成此用户数据的JWT
        User user = (User) authenticate.getPrincipal();
        log.debug("从认证结果中获取Principal={}", user.getClass().getName());
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtConst.KEY_USERNAME, user.getUsername());
        claims.put(JwtConst.KEY_PERMISSIONS, JSON.toJSONString(user.getAuthorities()));
        log.debug("即将向JWT中写入数据={}", claims);

        // JWT的组成部分：Header（头），Payload（载荷），Signature（签名）
        String jwt = Jwts.builder()
                // Header：指定算法与当前数据类型
                // 格式为： { "alg": 算法, "typ": "jwt" }
                .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                // Payload：通常包含Claims（自定义数据）和过期时间
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))
                // Signature：由算法和密钥（secret key）这2部分组成
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 打包生成
                .compact();
        return jwt;
    }

    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        // 获取需添加的用户名
        String username = adminAddNewDTO.getUsername();
        log.debug("需添加的用户名为:{}",username);
        // 根据用户名查询数据库，并判断结果是否不为空
        AdminSimpleVO resultByUsername = adminMapper.getByUsername(username);
        if (resultByUsername != null) {
            // 若果结果不为空，抛出用户名重复异常
            log.debug("添加管理员失败，用户名({})已存在!",username);
            throw new ServiceException(State.ERR_ADMIN_NAME_DUPLICATE,"添加管理员失败，用户名("+username+")已存在!");
        }

        // 获取需添加的手机号
        String phone = adminAddNewDTO.getPhone();
        log.debug("需添加的手机号为:{}",phone);
        // 根据手机号查询数据库，并判断结果是否不为空
        AdminSimpleVO resultByPhone = adminMapper.getByPhone(phone);
        if (resultByPhone != null) {
            // 若果结果不为空，抛出手机号重复异常
            log.debug("添加管理员失败，手机号({})已存在!",phone);
            throw new ServiceException(State.ERR_ADMIN_PHONE_DUPLICATE,"添加管理员失败，手机号("+phone+")已存在!");
        }

        // 创建Admin对象，将参数包含的属性信息复制到Admin对象
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewDTO,admin);
        // 获取当前时间，并赋值到gmtCreate和gmtModified属性
        LocalDateTime now = LocalDateTime.now();
        admin.setGmtCreate(now);
        admin.setGmtModified(now);
        log.debug("即将添加管理员Admin:{}",admin);

        // 调用mapper方法插入数据，并判断返回受影响的行数，若不为1，抛出异常
        int rows = adminMapper.insert(admin);
        if (rows != 1) {
            log.debug("添加管理员操作失败，即将抛出异常!");
            throw new ServiceException(State.ERR_INSERT,"添加管理员失败，系统忙，请稍后再试!");
        }
        log.debug("添加管理员成功!");
    }

    @Override
    public void matchRolesByAdminId(AdminMatchRolesDTO adminMatchRolesDTO) {
        // 获取需配置角色的管理员id
        Long adminId = adminMatchRolesDTO.getAdminId();
        log.debug("需配置角色的管理员id={}",adminId);
        // 通过管理员id查询管理员信息，并判断，如果为null，抛出异常
        AdminSimpleVO admin = adminMapper.getById(adminId);
        if (admin == null) {
            log.debug("管理员不存在，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"配置角色失败，管理员(id="+adminId+")不存在!");
        }

        // 获取需配置的角色id集合，并判断其isEmpty，若为true，抛出异常
        List<Long> roles = adminMatchRolesDTO.getRoles();
        log.debug("配置的角色id为{}",roles);
        if (roles == null || roles.isEmpty()) {
            log.debug("角色信息为空，即将抛出异常");
            throw new ServiceException(State.ERR_ROLE_EMPTY,"配置角色失败，角色信息为空!");
        }

        // 遍历角色id集合，通过角色id查询角色信息
        for (Long roleId : roles) {
            RoleListItemVO role = roleMapper.getById(roleId);
            // 判断角色信息是否为null，若为null则抛出异常
            if (role == null) {
                log.debug("角色(id={})不存在，即将抛出异常",roleId);
                throw new ServiceException(State.ERR_ROLE_NOT_FOUND,"配置角色失败，角色(id="+roleId+")不存在!");
            }
        }

        // 根据管理员id查询ms_admin_role表中原有匹配数据
        List<Long> rawRoles = adminMapper.getRolesIdById(adminId);
        if (!rawRoles.isEmpty()) {
            // 根据管理员id删除ms_admin_role表中原有匹配数据
            int rows = adminMapper.deleteRolesById(adminId);
            if (rows != rawRoles.size()) {
                throw new ServiceException(State.ERR_UPDATE,"配置角色失败，服务器忙，请稍后再试!");
            }
            log.debug("删除该管理员原有角色完成");
        }

        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        // 调用mapper方法，向数据库写入需配置的角色信息，并判断返回的行数
        int rows = adminMapper.insertRolesById(adminId, roles, now);
        if (rows != roles.size()) {
            throw new ServiceException(State.ERR_UPDATE,"配置角色失败，服务器忙，请稍后再试!");
        }

        // 修改gmt_modified字段值，更新修改时间
        updateGmtModifiedById(adminId);
        log.debug("为管理员({})配置角色完成",admin.getUsername());
    }

    @Override
    public void deleteById(Long id) {
        // 根据id查询ms_admin表中管理员信息
        AdminSimpleVO admin = adminMapper.getById(id);
        log.debug("根据id={}查询到结果Admin={}",id,admin);
        // 判断结果是否为null，若为null则抛出异常
        if (admin == null) {
            log.debug("管理员不存在，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"删除管理员失败，管理员(id="+id+")不存在!");
        }

        // 调用mapper删除ms_admin表中的管理员数据，并判断返回的行数，若不为1，则抛出异常
        log.debug("即将开始删除管理员数据:{}",admin);
        int rows = adminMapper.deleteById(id);
        if (rows != 1) {
            log.debug("删除数据失败，即将抛出异常");
            throw new ServiceException(State.ERR_DELETE,"删除管理员失败，系统忙，请稍后再试!");
        }
        log.debug("删除管理员数据成功");

        // 根据id查询ms_admin_role表中匹配角色id的集合，并得到集合的size
        List<Long> roles = adminMapper.getRolesIdById(id);
        log.debug("该管理员的角色为roles:{}",roles);
        int size = roles.size();
        // 判断size是否为0，若为0则结束方法
        if (size == 0) {
            return;
        }

        // 调用mapper删除ms_admin_role表中的匹配角色的数据，并判断返回的行数，若不等于size，则抛出异常
        rows = adminMapper.deleteRolesById(id);
        if (rows != size) {
            log.debug("删除数据失败，即将抛出异常");
            throw new ServiceException(State.ERR_DELETE,"删除管理员失败，系统忙，请稍后再试!");
        }
        log.debug("删除管理员的角色数据成功");
    }

    @Override
    public void disableById(Long id) {
        // 根据id查询管理员
        AdminSimpleVO admin = adminMapper.getById(id);
        log.debug("根据id({})查询结果Admin:{}",id,admin);
        // 判断管理员是否为null，如果是，抛出异常
        if (admin == null) {
            log.debug("管理员不存在，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"禁用管理员失败，管理员(id="+id+")不存在!");
        }
        // 判断管理员isEnable属性，如果为0，抛出异常
        Integer isEnable = admin.getIsEnable();
        if (isEnable == 0) {
            log.debug("管理员已经是禁用状态，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_ALREADY_DISABLE,"禁用管理员失败，管理员已经是禁用状态!");
        }
        // 禁用管理员,并判断返回受影响的行数，若不为1，抛出异常
        Integer disable = 0;
        int rows = adminMapper.updateIsEnableById(id, disable);
        if (rows != 1) {
            log.debug("禁用管理员操作失败，即将抛出异常!");
            throw new ServiceException(State.ERR_UPDATE,"禁用管理员失败，系统忙，请稍后再试!");
        }

        // 修改gmt_modified字段值，更新修改时间
        updateGmtModifiedById(id);
        log.debug("禁用管理员成功!");
    }

    @Override
    public void enableById(Long id) {
        // 根据id查询管理员
        AdminSimpleVO admin = adminMapper.getById(id);
        log.debug("根据id({})查询结果Admin:{}",id,admin);
        // 判断管理员是否为null，如果是，抛出异常
        if (admin == null) {
            log.debug("管理员不存在，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"启用管理员失败，管理员(id="+id+")不存在!");
        }
        // 判断管理员isEnable属性，如果为0，抛出异常
        Integer isEnable = admin.getIsEnable();
        if (isEnable == 1) {
            log.debug("管理员已经是启用状态，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_ALREADY_ENABLE,"启用管理员失败，管理员已经是启用状态!");
        }
        // 启用管理员,并判断返回受影响的行数，若不为1，抛出异常
        Integer enable = 1;
        int rows = adminMapper.updateIsEnableById(id, enable);
        if (rows != 1) {
            log.debug("启用管理员操作失败，即将抛出异常!");
            throw new ServiceException(State.ERR_UPDATE,"启用管理员失败，系统忙，请稍后再试!");
        }

        // 修改gmt_modified字段值，更新修改时间
        updateGmtModifiedById(id);
        log.debug("启用管理员成功!");
    }

    @Override
    public void updateInfo(AdminUpdateInfoDTO admin) {
        log.debug("想要修改的管理员信息为:Admin={}",admin);
        // 获取需要修改信息的管理员id
        Long id = admin.getId();
        // 通过id查询数据库中的管理员信息
        AdminSimpleVO resultById = adminMapper.getById(id);
        // 判断结果是否为null，若为null则抛出异常
        if (resultById == null) {
            log.debug("管理员(id={})不存在，即将抛出异常",id);
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"修改管理员信息失败，管理员(id="+id+")不存在!");
        }

        // 获取需要修改信息的管理员username
        String username = admin.getUsername();
        // 通过username查询数据库中的管理员信息
        AdminSimpleVO resultByUsername = adminMapper.getByUsername(username);
        // 判断查询结果的id与参数的id是否相等，若不等则抛出异常
        if (resultByUsername != null && !id.equals(resultByUsername.getId())) {
            log.debug("管理员(username={})已存在，即将抛出异常",username);
            throw new ServiceException(State.ERR_ADMIN_NAME_DUPLICATE,"修改管理员信息失败，管理员(username="+username+")已存在!");
        }

        // 获取需要修改信息的管理员phone
        String phone = admin.getPhone();
        // 通过phone查询数据库中的管理员信息
        AdminSimpleVO resultByPhone = adminMapper.getByPhone(phone);
        // 判断查询结果的id与参数的id是否相等，若不等则抛出异常
        if (resultByPhone != null && !id.equals(resultByPhone.getId())) {
            log.debug("管理员(phone={})已存在，即将抛出异常",phone);
            throw new ServiceException(State.ERR_ADMIN_PHONE_DUPLICATE,"修改管理员信息失败，管理员手机号(phone="+phone+")已存在!");
        }

        // 调用mapper方法更新管理员基本信息
        int rows = adminMapper.updateInfo(admin);
        if (rows != 1) {
            log.debug("修改管理员信息操作失败，即将抛出异常!");
            throw new ServiceException(State.ERR_UPDATE,"修改管理员信息失败，系统忙，请稍后再试!");
        }
        // 修改更新的时间
        updateGmtModifiedById(id);
        log.debug("修改管理员信息完成!");
    }

    @Override
    public AdminDetailVO getDetailsById(Long id) {
        // 根据id从数据库查询结果
        AdminDetailVO admin = adminMapper.getDetailsById(id);
        log.debug("根据id={}查询结果为Admin={}",id,admin);
        // 判断结果是否为null，如果是，抛出异常
        if (admin == null) {
            log.debug("管理员不存在，即将抛出异常");
            throw new ServiceException(State.ERR_ADMIN_NOT_FOUND,"查询管理员失败，管理员(id="+id+")不存在!");
        }
        return admin;
    }

    @Override
    public List<AdminListItemVO> list() {
        return adminMapper.list();
    }


    private void updateGmtModifiedById(Long id) {
        // 修改gmt_modified字段值，更新修改时间
        log.debug("开始修改更新时间");
        LocalDateTime gmtModified = LocalDateTime.now();
        int rows = adminMapper.updateGmtModifiedById(id, gmtModified);
        if (rows != 1) {
            log.debug("修改更新时间操作失败，即将抛出异常!");
            throw new ServiceException(State.ERR_UPDATE,"更新数据失败，系统忙，请稍后再试!");
        }
        log.debug("修改更新时间完成");
    }
}
